package edu.cmu.lti.algorithm.optimization;

import riso.numerical.LBFGS;

/**
 * This subroutine solves the unconstrained minimization problem
 * //http://riso.sourceforge.net/riso-tutorial.html
 * @author nlao
 *
 */
	
public abstract class LBFGSWraper {
	
	public static class Param	extends edu.cmu.lti.util.run.Param{
		private static final long serialVersionUID = 2008042701L; // YYYYMMDD
		//public String lang;
		
		/**
		 * diagco - Set this to true if the user wishes to provide 
		 * the diagonal matrix Hk0 at each iteration. 
		 * Otherwise it should be set to false in which case lbfgs 
		 * will use a default value described below. 
		 * If diagco is set to true the routine will return at each 
		 * iteration of the algorithm with iflag = 2, 
		 * and the diagonal matrix Hk0 must be provided in the array diag.	 */
		public boolean diagco = false;

		/**
		 *m - The number of corrections used in the BFGS update. 
		 *Values of m less than 3 are not recommended; 
		 *large values of m will result in excessive computing time. 
		 *3 <= m <= 7 is recommended. Restriction: m > 0. 
		 */
		public int m;
		
		public int niter_max;
		//public int m;

		/**
		 * eps - Determines the accuracy with which the solution is to be found. 
		 * The subroutine terminates when  ||G|| < EPS max(1,||X||),
	   *             where ||.|| denotes the Euclidean norm.
		 */
		public double eps;
		
		/** 

		iprint - Specifies output generated by lbfgs. 
			iprint[0] specifies the frequency of the output: 
			iprint[0] < 0: no output is generated, 
			iprint[0] = 0: output only at first and last iteration, 
			iprint[0] > 0: output every iprint[0] iterations. 
			
			iprint[1] specifies the type of output generated: 
			iprint[1] = 0: iteration count, number of function evaluations, 
					function value, norm of the gradient, and steplength, 
			iprint[1] = 1: same as iprint[1]=0, plus vector of variables 
					and gradient vector at the initial point, 
			iprint[1] = 2: same as iprint[1]=1, plus vector of variables, 
			iprint[1] = 3: same as iprint[1]=2, plus gradient vector. 

		*/
		public int[] iprint = new int[2];

		/**
		 * An estimate of the machine precision 
		 * (e.g. 10e-16 on a SUN station 3/60). 
		 * The line search routine will terminate 
		 * if the relative width of the interval of 
		 * uncertainty is less than xtol. 
		 */
		public double xtol;

		public Param(Class c) {
			super(c);
			parse();
		}		
		public void parse(){	
			m = getInt("m",5);
			niter_max = getInt("niter_max",10);
			eps = getDouble("eps",1e-5);
			//lang = getString("lang");
			diagco=getBoolean("diagco", false);
			xtol = getDouble("xtol",1e-16);	// double precision machine epsilon
			
			iprint[0] = 1;						// give output on every iteration
			iprint[1] = 0;
		}	
	}	
	public Param p;
	public LBFGSWraper(Class c){
		p=new Param(c);
	}
	



	/**The number of variables in the minimization problem. 
	Restriction: n > 0. */
	//public int n;
	
	/**
	 * x - On initial entry this must be set by the user 
	 *	to the values of the initial estimate of the solution vector. 
	 *	On exit with iflag = 0, it contains the values of the variables
	 *	at the best point found (usually a solution).
	 *
	 * iflag - This must be set to 0 on initial entry to lbfgs. 
	 * A return with 
		iflag < 0 indicates an error
		iflag = 0 indicates that the routine has terminated without
				detecting errors. 
		iflag = 1, the user	must evaluate the function f and gradient g. 
		iflag = 2, the user must provide the diagonal matrix Hk0. 
		
		The following negative values of iflag, detecting an error, are possible: 
		iflag = -1 The line search routine mcsrch failed. 
			One of the following messages is printed: 
				Improper input parameters. 
				Relative width of the interval of uncertainty is at most xtol. 
				More than 20 function evaluations were required at the present iteration. 
				The step is too small. 
				The step is too large. 
				Rounding errors prevent further progress. 
				There may not be a step which satisfies the sufficient 
					decrease and curvature conditions. 
					Tolerances may be too small. 
		iflag = -2 The i-th diagonal element of the diagonal inverse Hessian approximation, given in DIAG, is not positive. 
		iflag = -3 Improper input parameters for LBFGS (n or m are not positive). 
   */	
	//public double[] x;
	
	public int optimize(double[] x){	
		int nwts = x.length;
		double[] diag = new double[nwts];		
		/*diag - If diagco = true, then on initial entry or on re-entry with iflag = 2, 
		 * diag must be set by the user to contain the values of the diagonal matrix Hk0. 
		 * Restriction: all elements of diag must be positive.
		 */
		
		int[] viflag = new int[1];
		viflag[0] = 0;				

		for ( int i = 0; i < p.niter_max 
			&& (i == 0 || viflag[0] != 0); ++i )	{
			setx(x);
			try		{
				LBFGS.lbfgs( nwts, p.m,x, 
					getValue(),getGrad(),					
					p.diagco, diag, 
					p.iprint, p.eps,p.xtol,viflag
					);
			}
			catch (Exception e)		{
				e.printStackTrace();
				System.err.print(e);
			}		
		}
		return viflag[0];
	}	
	abstract protected void setx(double[] x);
	
	 /**
	  *f - Before initial entry and on a re-entry with iflag = 1, 
	  *it must be set by the user to contain the value of the 
	  *function f at the point x. 
	  */	
		abstract protected double getValue();
		
		/**
		 * g - Before initial entry and on a re-entry with iflag = 1, 
		 * it must be set by the user to contain the components of 
		 * the gradient g at the point x.
		 */
		abstract protected double[] getGrad();
		
}
